package model;

import java.util.ArrayList;
import java.util.List;

/*
 * 
 * Model class
 * 
 * Responsible for storing and manipulating game data.
 * 
 */

public class Model implements IModel {

	public enum Status {
		ALIVE, DEAD, GAMEOVER;
	}

	private LevelManager lvlManager;
	private List<IModelObserver> observers = new ArrayList<IModelObserver>();
	private int currentLives = 3;
	private boolean isLoadingLevel;
	private int victoryBlockLocationX;
	private int victoryBlockLocationY;

	private Status status = Status.ALIVE;

	/**
	 * Model
	 * 
	 * creates a model object
	 */
	public Model() {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#checkVictory()
	 */
	@Override
	public boolean checkVictory() {
		int currentBlockX = lvlManager.getWorldHead() + 400;
		int currentBlockY = lvlManager.getYPosition();
		int levelLength = lvlManager.getLevelLength();

		int row = 11 - currentBlockY / 50;
		int column = (1 + currentBlockX / 50) % levelLength;
		if ((row == (victoryBlockLocationX + 1)
				|| row == (victoryBlockLocationX - 1) || row == victoryBlockLocationX)
				&& (column == (victoryBlockLocationY + 1)
				|| column == (victoryBlockLocationY - 1) || column == victoryBlockLocationY)) {
			setDirty();
			return true;
		}

		return false;
	}

	/**
	 * findVictoryLocation
	 * 
	 * finds the location the of the victory block, so that it can be used to
	 * determine if the level is complete
	 * 
	 * @param blockList
	 */
	private void findVictoryLocation(LevelEntity blockList[][]) {
		for (int i = 0; i < 12; i++) {
			for (int j = 0; j < blockList.length; j++) {
				if (blockList[j][i] != null
						&& blockList[j][i].getType() == Block.VICTORY) {
					victoryBlockLocationX = i;
					victoryBlockLocationY = j;
				}
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getBlocks()
	 */
	public LevelEntity[][] getBlocks() {
		return lvlManager.getBlocks();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getCurrentLemonSprite()
	 */
	@Override
	public int getCurrentLemonSprite() {
		return lvlManager.getCurrentLemonSprite();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getCurrentLevel()
	 */
	@Override
	public int getCurrentLevel() {
		return lvlManager.getLevelNumber();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getCurrentLives()
	 */
	@Override
	public int getCurrentLives() {
		return currentLives;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getHealth()
	 */
	@Override
	public int getHealth() {
		return lvlManager.getHealth();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getLoadingLevel()
	 */
	@Override
	public boolean getLoadingLevel() {
		return isLoadingLevel;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getPauseState()
	 */
	public boolean getPauseState() {
		return lvlManager.getPauseState();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getPlayerHeight()
	 */
	public int getPlayerHeight() {
		return lvlManager.getPlayerHeight();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getStatus()
	 */
	@Override
	public Status getStatus() {
		return status;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#getWorldHead()
	 */
	public int getWorldHead() {
		return lvlManager.getHead();
	}

	// The coordinates in this are 0,0 in the lower left hand corner
	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#loadLevel(int)
	 */
	public boolean loadLevel(int nextLevel) {
		setDirty();
		// array follows form [column/xPos][row/yPos]
		LevelEntity blockList[][];
		switch (nextLevel) {
		case 1: {
			blockList = new LevelEntity[60][12];
			for (int i = 0; i < blockList.length; i++) {
				if (i < 40 || i > 42) {
					blockList[i][0] = new LevelEntity(i, 0, Block.DIRT, 0);
					blockList[i][1] = new LevelEntity(i, 1, Block.GRASS, 0);
				}
				if (i > 20 && i < 30) {
					blockList[i][1] = new LevelEntity(i, 1, Block.DIRT, 0);
					blockList[i][2] = new LevelEntity(i, 2, Block.GRASS, 0);
				}
			}
			blockList[6][6] = new LevelEntity(6, 6, Block.SPIKE, 1);
			blockList[12][3] = new LevelEntity(12, 3, Block.SPIKE, 1);
			blockList[19][5] = new LevelEntity(15, 5, Block.SPIKE, 1);
			blockList[27][9] = new LevelEntity(27, 9, Block.SPIKE, 1);
			blockList[36][6] = new LevelEntity(36, 6, Block.SPIKE, 1);
			blockList[45][3] = new LevelEntity(42, 3, Block.SPIKE, 1);
			blockList[49][3] = new LevelEntity(49, 4, Block.GRASS, 0);
			blockList[51][6] = new LevelEntity(51, 6, Block.GRASS, 0);
			blockList[49][9] = new LevelEntity(49, 4, Block.GRASS, 0);
			for (int i = 2; i < 9; i++) {
				blockList[53][i] = new LevelEntity(53, i, Block.SPIKE, 1);
			}
			blockList[45][2] = new LevelEntity(45, 2, Block.HEALTH, -1);
			blockList[59][2] = new LevelEntity(59, 2, Block.VICTORY, 0);
			lvlManager = new LevelManager(2, 250, blockList, 1);

			findVictoryLocation(blockList);

			return true;
		}
		case 2: {
			blockList = new LevelEntity[30][12];
			for (int i = 0; i < blockList.length; i++) {
				blockList[i][0] = new LevelEntity(i, 0, Block.GRASS, 0);
				if (i % 5 == 1 || i % 5 == 2)
					blockList[i][4] = new LevelEntity(i, 4, Block.SPIKE, 1);
				if (i % 5 == 0)
					blockList[i][4] = new LevelEntity(i, 4, Block.GRASS, 0);
			}
			blockList[25][5] = new LevelEntity(25, 5, Block.VICTORY, 0);
			blockList[9][2] = new LevelEntity(9, 2, Block.GRASS, 0);
			lvlManager = new LevelManager(2, 250, blockList, 2);
			findVictoryLocation(blockList);
			return true;
		}
		default: {
			blockList = new LevelEntity[30][12];
			for (int i = 0; i < blockList.length; i++) {
				blockList[i][3] = new LevelEntity(i, 0, Block.GRASS, 0);
			}
			blockList[24][4] = new LevelEntity(25, 5, Block.VICTORY, 0);
			lvlManager = new LevelManager(2, 250, blockList, 99);
			findVictoryLocation(blockList);
			return true;
		}
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#loadNextLevel()
	 */
	@Override
	public boolean loadNextLevel() {
		return loadLevel(lvlManager.getLevelNumber() + 1);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#moveJump()
	 */
	public void moveJump() {
		lvlManager.moveJump();
		return;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#moveLeft()
	 */
	public void moveLeft() {
		lvlManager.moveLeft();
		return;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#moveRight()
	 */
	public void moveRight() {
		lvlManager.moveRight();
		return;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#moveStop()
	 */
	public void moveStop() {
		lvlManager.moveStop();
		return;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#registerWithModel(model.IModelObserver)
	 */
	public void registerWithModel(IModelObserver observer) {
		this.observers.add(observer);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#revivePlayer()
	 */
	public void revivePlayer() {
		status = Status.ALIVE;
	}

	/**
	 * setDirty
	 * 
	 * checks to see if the model has changed, so that it can be repainted
	 */
	public void setDirty() {
		for (IModelObserver observer : observers) {
			observer.modelChanged();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#setLoadingLevel(boolean)
	 */
	@Override
	public void setLoadingLevel(boolean loadingLevel) {
		isLoadingLevel = loadingLevel;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#togglePause()
	 */
	public void togglePause() {
		lvlManager.togglePause();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#unregisterWithModel(model.IModelObserver)
	 */
	public void unregisterWithModel(IModelObserver observer) {
		this.observers.remove(observer);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.IModel#update()
	 */
	public void update() {
		lvlManager.update();
		if (lvlManager.checkDeath()) {
			currentLives--;
			if (currentLives == 0) {
				status = Status.GAMEOVER;
				currentLives = 3;
			} else
				status = Status.DEAD;
		}
		this.setDirty();
		return;
	}
}